﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TimbreRecognition.Recognition.Model;
using TimbreRecognition.Recognition.Model.Kohenen;

namespace TimbreRecognition.Recognition
{
   public class NeuronNetworkFactory {

        private static readonly Random random = new Random();

        public static NeuronNetwork create(int numberOfInputs, int numberOfOutputs, int[] hiddenLayers)
        {
            NeuronNetwork network = new NeuronNetwork();
            NeuronLayer inputLayer = createLayer(numberOfInputs, hiddenLayers[0]);
            network.addLayer(inputLayer);

            for (int i = 0; i < hiddenLayers.Length - 1; i++)
            {
                NeuronLayer hiddenLayer = createLayer(hiddenLayers[i], hiddenLayers[i + 1]);
                network.addLayer(hiddenLayer);
            }

            NeuronLayer outputLayer = createLayer(hiddenLayers[hiddenLayers.Length - 1], numberOfOutputs);
            network.addLayer(outputLayer);
            return network;
        }

        private static NeuronLayer createLayer(int inputCount, int outputCount) {
            List<Neuron> neurons = new List<Neuron>();
            for (int i = 0; i < outputCount; i++) {
                Neuron neuron = createNeuron(inputCount);
                neurons.Add(neuron);
            }
            NeuronLayer layer = new NeuronLayer();
            layer.setNeurons(neurons);
            return layer;
        }

        private static Neuron createNeuron(int inputCount) {

            const int normalizationFactor =  5;
            const double decreaseFactor =  0.1;

            double[] weights = new double[inputCount];
            for (int i = 0; i < weights.Length; i++) {
                weights[i] = random.NextDouble()/ normalizationFactor - decreaseFactor;
            }

            Neuron neuron = new Neuron();
            neuron.setWeights(weights);
            neuron.setBias(random.NextDouble() / normalizationFactor - decreaseFactor);
            neuron.setDeltas(new double[inputCount]);

            return neuron;
        }

        public static KohenenNetwork createKonenanNetwork(int width, int height, int inputCount)
        {
            const int normalizationFactor =  2;
            const double decreaseFactor =  0.0;

            KohenenNetwork network = new KohenenNetwork();
            KohenenNeuron[][] neurons = new KohenenNeuron[width][];
            for (int x = 0; x < width; x++) {
                KohenenNeuron[] line = new KohenenNeuron[height];
                neurons[x] = line;
                for (int y = 0; y < height; y++){

                    KohenenNeuron neuron = new KohenenNeuron();

                    double[] weights = new double[inputCount];
                    for (int i = 0; i < weights.Length; i++) {
                        weights[i] = random.NextDouble()/ normalizationFactor - decreaseFactor;
                    }

                    neuron.setWeights(weights);

                    neuron.setX(x);
                    neuron.setY(y);

                    neurons[x][y] = neuron;
                }
            }

            network.setNeurons(neurons);

            return network;
        }
   }
}
